using System;
using System.Linq.Expressions;

namespace Picnic.Spec.Utils
{
	/// <summary>
	/// Represents a value validator.
	/// </summary>
	internal class ValueValidator<T>
	{
		private readonly Expression<Func<T>> _valueExpression;
		private readonly object _value;

		/// <summary>
		/// Initializes a new instance of the <see cref="ValueValidator{T}"/> class.
		/// </summary>
		internal ValueValidator(object value)
		{
			_value = value;
		}

		/// <summary>
		/// Initializes a new instance of the <see cref="ValueValidator{T}"/> class.
		/// </summary>
		internal ValueValidator(Expression<Func<T>> expression)
		{
			var compiledExpression = expression.Compile();

			_value = compiledExpression.Invoke();
			_valueExpression = expression;
		}

		/// <summary>
		/// Throws <see cref="ArgumentException"/> if value is <see langword="null"/>.
		/// </summary>
		internal void IsNotNull(string message = null)
		{
			if (Value == null)
			{
				var argumentName = GetArgumentNameFromExpression();
				if (string.IsNullOrEmpty(message))
				{
					message = "Value can not be null.";
				}
				if (string.IsNullOrEmpty(argumentName))
				{
					throw new ArgumentException(message);
				}
				throw new ArgumentException(message, argumentName);
			}
		}

		/// <summary>
		/// Gets a value.
		/// </summary>
		protected object Value
		{
			get { return _value; }
		}

		/// <summary>
		/// Returns an argument name extracted from expression.
		/// </summary>
		protected string GetArgumentNameFromExpression()
		{
			if (_valueExpression == null)
			{
				return string.Empty;
			}
			if (_valueExpression.Body.NodeType == ExpressionType.MemberAccess)
			{
				return ((MemberExpression)_valueExpression.Body).Member.Name;
			}
			throw new InvalidOperationException("Unexpected expression. Member access only supported now.");
		}
	}

	internal class StringValueValidator : ValueValidator<string>
	{
		/// <summary>
		/// Initializes a new instance of the <see cref="StringValueValidator"/> class.
		/// </summary>
		internal StringValueValidator(string value)
			: base(value)
		{
		}

		/// <summary>
		/// Initializes a new instance of the <see cref="StringValueValidator"/> class.
		/// </summary>
		internal StringValueValidator(Expression<Func<string>> expression)
			: base(expression)
		{
		}

		/// <summary>
		/// Throws <see cref="ArgumentException"/> if values is <see langword="null"/> or empty string.
		/// </summary>
		internal void IsNotNullOrEmpty(string message = null)
		{
			if (string.IsNullOrEmpty((string)Value))
			{
				var argumentName = GetArgumentNameFromExpression();
				if (string.IsNullOrEmpty(message))
				{
					message = "String value can not be null or empty.";
				}
				if (string.IsNullOrEmpty(argumentName))
				{
					throw new ArgumentException(message);
				}
				throw new ArgumentException(message, argumentName);
			}
		}
	}

	/// <summary>
	/// Provides a set of argument values validation.
	/// </summary>
	internal static class Ensure
	{
		/// <summary>
		/// Returns a validator for specified value.
		/// </summary>
		internal static ValueValidator<object> That(object value)
		{
			return new ValueValidator<object>(value);
		}

		/// <summary>
		/// Returns a validator for specified value.
		/// </summary>
		internal static ValueValidator<object> That(Expression<Func<object>> expression)
		{
			return new ValueValidator<object>(expression);
		}

		/// <summary>
		/// Returns a validator for specified value.
		/// </summary>
		internal static StringValueValidator That(string value)
		{
			return new StringValueValidator(value);
		}

		/// <summary>
		/// Returns a validator for specified value.
		/// </summary>
		internal static StringValueValidator That(Expression<Func<string>> expression)
		{
			return new StringValueValidator(expression);
		}
	}
}